/*******************************************************************\
| Vector2.h
|
|--------------------------------------------------------------------
| Copyright 2005. Martin Fleisz.
| All rights reserved.
|
|--------------------------------------------------------------------
| CREATED:		2006/2/21
| AUTHOR:		Martin Fleisz
|
|--------------------------------------------------------------------
| DESCRIPTION:
| 
| 2 component vector class
\********************************************************************/
#ifndef VECTOR2_H
#define VECTOR2_H

#include "MathBase.h"

namespace Math
{

//*****************************************************************************
// class overview:
// @short:
//  2 component vector class
// @detailed:
//  Vector class for vector calculations in two dimensional space.
//-----------------------------------------------------------------------------
class Vector2
{
	public:		
		// the vector's components
		union
		{
			struct
			{
				float m_X;
				float m_Y;
			};
			float m_Xy[2];
		};

	public:
		// constructors
		inline Vector2();
		explicit inline Vector2(const float* p_Xy);
		inline Vector2(const Vector2& p_Vec);
		inline Vector2(const float p_X, const float p_Y);

	public:

		// +/- operator (vector sign)
		inline Vector2		operator+() const;
		inline Vector2		operator-() const;

		// +=, -=, *=, /= operators
		inline Vector2&	operator+=(const Vector2& p_Vec);
		inline Vector2&	operator-=(const Vector2& p_Vec);
		inline float		operator*=(const Vector2& p_Vec);
		inline Vector2&	operator*=(const float p_Value);
		inline Vector2&	operator/=(const float p_Value);

		// vector addition, substraction and dot product
		inline friend const Vector2	operator+(const Vector2& p_Vec1, const Vector2& p_Vec2);
		inline friend const Vector2	operator-(const Vector2& p_Vec1, const Vector2& p_Vec2);
		inline friend float				operator*(const Vector2& p_Vec1, const Vector2& p_Vec2);

		// scalar multiplication
		inline friend const Vector2	operator*(const Vector2& p_Vec, const float p_Value);
		inline friend const Vector2	operator*(const float p_Value, const Vector2& p_Vec);

		// scalar division
		inline friend const Vector2	operator/(const Vector2& p_Vec, const float p_Value);
		inline friend const Vector2	operator/(const float p_Value, const Vector2& p_Vec);

		// assignment operator
		inline Vector2&	operator=(const Vector2& p_Vec);

		// compare operators
		inline friend bool	operator==(const Vector2& p_Vec1, const Vector2& p_Vec2);
		inline friend bool	operator<(const Vector2& p_Vec1, const Vector2& p_Vec2);

	public:

		// set the vector's components
		inline void		Set(const float* p_Xy);
		inline void		Set(const Vector2& p_Vec);
		inline void		Set(const float p_X, const float p_Y);

		// zeros the vector
		inline void		Zero();

		// return length of the vector (or square of the length for LengthSqr)
		inline float	Length()	const;
		inline float	LengthSqr()	const;        

		// normalize vector (the Fast function uses the reciprocal sqrt function
		// but is not as accurate as the standard Normalize function)
		inline void		Normalize();
		inline void		NormalizeFast();

		// interpolates p_Vec1 and p_Vec2 and stores the result in the current vector
		inline void		Interpolate(const Vector2& p_Vec1, const Vector2& p_Vec2, const float p_Value);

	public:
		// special predefined values
		static const Vector2 ZERO;
		static const Vector2 INFINITY;
		static const Vector2 UNIT_X;
		static const Vector2 UNIT_Y;
};

/* ***************************************************************** *\
|  INLINE FUNCTIONS
\* ***************************************************************** */
inline Vector2::Vector2() 
{ 
}

inline Vector2::Vector2(const float p_X, const float p_Y) 
{ 
	m_X = p_X;
	m_Y = p_Y;
}

inline Vector2::Vector2(const float* p_Xy) 	
{ 
	m_X = p_Xy[0];
	m_Y = p_Xy[1];
}

inline Vector2::Vector2(const Vector2& p_Vec)
{
	m_X = p_Vec.m_X;
	m_Y = p_Vec.m_Y;
}

// + operator (vector sign)
inline Vector2 Vector2::operator+() const
{
	return (*this);
}

// - operator (vector sign)
inline Vector2 Vector2::operator-() const
{
	return Vector2(-m_X, -m_Y);
}

// += operator
inline Vector2& Vector2::operator+=(const Vector2& p_Vec)
{
	m_X += p_Vec.m_X;
	m_Y += p_Vec.m_Y;

	return (*this);
}

// -= operator
inline Vector2& Vector2::operator-=(const Vector2& p_Vec)
{
	m_X -= p_Vec.m_X;
	m_Y -= p_Vec.m_Y;

	return (*this);
}

// dot product
inline float Vector2::operator*=(const Vector2& p_Vec)
{
	return ((m_X * p_Vec.m_X) + (m_Y * p_Vec.m_Y));
}

// scalar multiplication
inline Vector2& Vector2::operator*=(const float p_Value)
{
	m_X *= p_Value;
	m_Y *= p_Value;

	return (*this);
}

// /= operator
inline Vector2& Vector2::operator/=(const float p_Value)
{
	const float invValue = 1.0f / p_Value;
	m_X *= invValue;
	m_Y *= invValue;

	return (*this);
}

// assignment operator
inline Vector2& Vector2::operator=(const Vector2& p_Vec)
{
	m_X = p_Vec.m_X;
	m_Y = p_Vec.m_Y;

	return (*this);
}

// friend function for addition, substraction and dot product
inline const Vector2 operator+(const Vector2& p_Vec1, const Vector2& p_Vec2)
{
	return Vector2(p_Vec1.m_X + p_Vec2.m_X, p_Vec1.m_Y + p_Vec2.m_Y);
}

inline const Vector2 operator-(const Vector2& p_Vec1, const Vector2& p_Vec2)
{
	return Vector2(p_Vec1.m_X - p_Vec2.m_X, p_Vec1.m_Y - p_Vec2.m_Y);
}

inline float operator*(const Vector2& p_Vec1, const Vector2& p_Vec2)
{
	return ((p_Vec1.m_X * p_Vec2.m_X) + (p_Vec1.m_Y * p_Vec2.m_Y));
}

// vector/scalar multiplication
inline const Vector2 operator*(const Vector2& p_Vec, const float p_Value)
{
	return Vector2(p_Vec.m_X * p_Value, p_Vec.m_Y * p_Value);
}

inline const Vector2 operator*(const float p_Value, const Vector2& p_Vec)
{
	return Vector2(p_Vec.m_X * p_Value, p_Vec.m_Y * p_Value);
}

// vector/scalar division
inline const Vector2 operator/(const Vector2& p_Vec, const float p_Value)
{
	const float invValue = 1.0f / p_Value;
	return Vector2(p_Vec.m_X * invValue, p_Vec.m_Y * invValue);
}

inline const Vector2 operator/(const float p_Value, const Vector2& p_Vec)
{
	const float invValue = 1.0f / p_Value;
	return Vector2(p_Vec.m_X * invValue, p_Vec.m_Y * invValue);
}

// comparison operators
inline bool operator==(const Vector2& p_Vec1, const Vector2& p_Vec2)
{
	if(Abs(p_Vec1.m_X - p_Vec2.m_X) < EPSILON && Abs(p_Vec1.m_Y - p_Vec2.m_Y) < EPSILON)
		return true;

	return false;
}

inline bool operator<(const Vector2& p_Vec1, const Vector2& p_Vec2)
{
	if(p_Vec1.LengthSqr() < p_Vec2.LengthSqr())
		return true;

	return false;
}

// set vector from a float pointer
inline void Vector2::Set(const float* p_Xy)
{
	m_X = p_Xy[0];
	m_Y = p_Xy[1];
}

// copy vector from another vector
inline void Vector2::Set(const Vector2& p_Vec)
{
	m_X = p_Vec.m_X;
	m_Y = p_Vec.m_Y;
}

// set vector components
inline void Vector2::Set(const float p_X, const float p_Y)
{
	m_X = p_X;
	m_Y = p_Y;
}

// zeros all vector components
inline void Vector2::Zero()
{
	m_X = m_Y = 0.0f;
}

// length function
inline float Vector2::Length() const
{
	return Sqrt(Sqr(m_X) + Sqr(m_Y));
}

// returns the square of the length (used for == comparison)
inline float Vector2::LengthSqr() const
{
	return Sqr(m_X) + Sqr(m_Y);
}

// normalizes the vector (make a unit vector)
inline void Vector2::Normalize()
{
	const float length = Length();
	if(length > Math::EPSILON)
	{
		const float invLength = 1.0f / length;
		m_X *= invLength;
		m_Y *= invLength;
	}
}

// normalizes the vector (make a unit vector) using the fast reciprocal sqrt function
inline void Vector2::NormalizeFast()
{
	const float invLength = InvSqrt(LengthSqr());
	m_X *= invLength;
	m_Y *= invLength;
}

// interpolate the two vectors and store the result in the current intance
inline void Vector2::Interpolate(const Vector2& p_Vec1, const Vector2& p_Vec2, const float p_Value)
{
	const float oneMinusValue = 1.0f - p_Value;

	m_X = (p_Vec1.m_X * oneMinusValue) + (p_Vec2.m_X * p_Value);
	m_Y = (p_Vec1.m_Y * oneMinusValue) + (p_Vec2.m_Y * p_Value);
}

} // namespace Math

#endif // VECTOR2_H
